package org.aceor.mddal.client.jdbc;

import com.google.common.collect.Lists;
import org.aceor.mddal.client.jdbc.adapter.AbstractPreparedStatementAdapter;
import org.aceor.mddal.common.sqlparser.result.SQLParsedResult;
import org.aceor.mddal.gateway.executor.ExecuteContext;
import org.aceor.mddal.gateway.executor.PreparedStatementExecutor;
import org.aceor.mddal.gateway.router.RealSqlContext;
import org.aceor.mddal.gateway.router.Router;
import org.aceor.mddal.gateway.sqlparser.SQLParserFactory;
import org.aceor.mddal.group.GroupPreparedStatement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * Created by lxue on 16/4/5.
 */
public class PreparedStatementFacade extends AbstractPreparedStatementAdapter implements PreparedStatement {
    private static final Logger log = LoggerFactory.getLogger(PreparedStatementFacade.class);

    private String originSql;

    public PreparedStatementFacade(DataSourceFacade dataSourceFacade, ConnectionFacade connectionFacade, String sql) {
        super(dataSourceFacade, connectionFacade);
        this.originSql = sql;
    }

    private void setParameters(final PreparedStatement preparedStatement, final List<Object> parameters) throws SQLException {
        if (preparedStatement instanceof GroupPreparedStatement) {
            ((GroupPreparedStatement) preparedStatement).setParameters(parameters);
        } else {
            //不会走到这
            throw new IllegalArgumentException();
        }
    }

    @Override
    public ResultSet executeQuery() throws SQLException {
        return createPrepareStatementExecutor().executeQuery();
    }

    @Override
    public int executeUpdate() throws SQLException {
        return createPrepareStatementExecutor().executeUpdate();
    }

    @Override
    public boolean execute() throws SQLException {
        return createPrepareStatementExecutor().execute();
    }

    /**
     * 核心功能
     *
     * @return
     * @throws SQLException
     */
    private PreparedStatementExecutor createPrepareStatementExecutor() throws SQLException {

        /**
         * 获取路由信息.主要是分库分表的规则
         */
        Router router =
                Router.getInstance(dataSourceFacade.getLogicTable());
        /**
         * sql解析
         */
        SQLParsedResult sqlParsedResult =
                SQLParserFactory
                        .createSQLParserEngine(
                                originSql,
                                parameters,
                                router.getAllTableShardingColumnsMapping())
                        .parse();

        /**
         * 通过路由计算得到执行上下文
         */
        ExecuteContext executeContext =
                router.route(
                        sqlParsedResult,
                        originSql,
                        parameters);
        log.info("ExecuteContext :{}", executeContext);

        /**
         * 计算sql与对应执行的Statement
         */
        Collection<PreparedStatement> statements =
                buildPrepareStatements(executeContext);

        return PreparedStatementExecutor
                .build()
                .setExecuteContext(executeContext)
                .setStatementEntities(statements)
                .create();
    }

    private Collection<PreparedStatement> buildPrepareStatements(ExecuteContext executeContext) throws SQLException {
        Collection<PreparedStatement> statements = Lists.newLinkedList();

        for (Map.Entry<String, List<RealSqlContext>> entry : executeContext.sqlMap.entrySet()) {
            Connection connection = connectionFacade.getConnection(entry.getKey());
            if (null != connection) {
                PreparedStatement ps;
                for (RealSqlContext realSqlContext : entry.getValue()) {
                    ps = connection.prepareStatement(realSqlContext.sql);
//                    setParameters(ps, parameters);
//                    ==> 占位符都被替换掉了,这里set倒会使得group层又setParameter,由于已经没有占位符,进而报错.故注释掉了
                    //理论上这里sql的?占位符已经被替换了
                    if (realSqlContext.sql.contains("?")) {
                        throw new IllegalArgumentException("sql :" + realSqlContext.sql + "含有占位符");
                    }
                    statements.add(ps);
                }
            } else {
                log.error("没有获取到连接, groupDsKey :{}, groupDsMap :{}", entry.getKey(), dataSourceFacade.getDsMapHolder().get());
            }
        }

        return statements;
    }
}
